IBM®
Skip to main content
    Country/region [select]      Terms of use
 
 
      
     Home      Products      Services & solutions      Support & downloads      My account     

developerWorks > Linux >
developerWorks
Port Windows IPC apps to Linux, Part 1: Processes and threads
e-mail it!
Contents:
Processes
Creating a process
Terminating a process
Using wait functions
Exiting a process
Threads
Creating a thread
Specifying the thread function
Parameter passing to the thread function
Setting the stack size
Thread states
Changing priority
Examples of processes and threads
Next in the series
Resources
About the authors
Rate this article
Related content:
Basic use of pthreads
POSIX threads explained
Migrate your apps from OS/2 to Linux
Subscriptions:
dW newsletters
A mapping guide for complex, multithreaded, multiprocess applications

Level: Advanced

Srinivasan S. Muthuswamy (mailto:smuthusw@in.ibm.com?subject=Processes and threads), Software Engineer, IBM Global Services Group
Kavitha Varadarajan (mailto:vkavitha@in.ibm.com?subject=Processes and threads), Software Engineer, IBM India Software Lab

14 Apr 2005

The wave of migration to open source in business has the potential to cause a tremendous porting traffic jam as developers move the ever-pervasive Windows® application to the Linux™ platform. In this three-part series, get a mapping guide, complete with examples, to ease your transition from Windows to Linux. Part 1 introduces processes and threads.

Today many global businesses and services are going open source -- all the major corporate players in the industry are pushing for it. This trend has spurred a major migration exercise in which lots of existing products maintained for various platforms (Windows, OS2, Solaris, etc.) will be ported to open source Linux platforms.

Many applications are designed without considering the need to port them to Linux. This has the potential to be a porting nightmare, but it doesn't have to be. The goal of this series of articles is to help you migrate complex applications involving IPC and threading primitives from Windows to Linux. We share our experiences in moving these critical Windows IPC applications, applications that include multithreaded apps that require thread synchronization and multiprocess apps that require interprocess synchronization.

In short, think of this series as a mapping document -- it provides mapping of various Windows calls to Linux calls related to threads, processes, and interprocess communication elements (mutexes, semaphores, etc.). We've divided the mapping into three chunks:

  • Part 1 deals with processes and threads.
  • Part 2 handles semaphores and events.
  • Part 3 covers mutexes, critical sections, and wait functions.

Processes
Basic execution units in Windows and Linux are different. In Windows, the thread is the basic execution unit, and the process is a container that holds this thread.

In Linux, the basic execution unit is the process. The functionalities offered by Windows APIs can be mapped directly to Linux system calls:

Table 1. Process mapping
Windows Linux Classification
CreateProcess()
CreateProcessAsUser()
fork()
setuid()
exec()
Mappable
TerminateProcess() kill() Mappable
SetThreadpriority()
GetThreadPriority()
Setpriority()
getPriority()
Mappable
GetCurrentProcessID() getpid() Mappable
Exitprocess() exit() Mappable
Waitforsingleobject()
Waitformultipleobject()
GetExitCodeProcess()
waitpid()
Using Sys V semaphores, Waitforsingleobject/multipleobject
can be implemented
Context specific
GetEnvironmentVariable
SetEnvironmentVariable
getenv()
setenv()
Mappable

The Classification column (which explains classification constructs used in this article) indicates whether the Windows construct is mappable or context specific:

  • If mappable, the Windows construct can be mapped to the specified Linux construct(s) by closely examining the types, parameters, return codes, and such. Both the Windows and Linux constructs provide similar functionality.
  • If context specific, the given Windows construct may or may not have an equivalent construct in Linux, or Linux may have more than one construct that provides similar functionality. In either case, the decision to use a specific Linux construct(s) depends on the application context.

Creating a process
In Windows, you can use CreateProcess() to create a new process. The CreateProcess() function creates a new process and its main thread as follows:


BOOL CreateProcess(
 LPCTSTR lpApplicationName,                  // name of executable module
  LPTSTR lpCommandLine,                      // command line string
  LPSECURITY_ATTRIBUTES lpProcessAttributes, // SD
  LPSECURITY_ATTRIBUTES lpThreadAttributes,  // SD
  BOOL bInheritHandles,                      // handle inheritance option
  DWORD dwCreationFlags,                     // creation flags
  LPVOID lpEnvironment,                      // new environment block
  LPCTSTR lpCurrentDirectory,                // current directory name
  LPSTARTUPINFO lpStartupInfo,               // startup information
  LPPROCESS_INFORMATION lpProcessInformation // process information
)

bInheritHandles determines whether the handles have to be inherited to the child from the parent. lpApplicationName and lpCommandLine give the name and path of the process to be started. lpEnvironment defines the environment that has to be visible for the process.

In Linux, the exec* family of functions replace the current process image with a new process image (as shown in the following):


int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int  execle(const  char  *path,  const  char  *arg  , ..., char * const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);

These versions of exec* are just various calling interfaces for core function execve(): int execve(const char *filename, char *const argv [], char *const envp[]). Here argv is the pointer containing arguments list and envp is the pointer containing list of environment variables which are basically key=value pairs.

This must be used along with the fork() command so both the parent and child processes are running: pid_t fork(void). fork() creates a child process that differs from the parent process only in its PID and PPID; in fact, the resource utilizations are set to 0.

By default, the exec() function inherits the group and user IDs from the parent process, which makes it dependent on the parent process. This can be changed by:

  • Setting the set-uid and set-gid bit on the program file pointed
  • Using the setpgid() and setuid() system call

The CreateProcessAsUser() function is similar to CreateProcess() except that the new process runs in the security context of the user represented by the hToken parameter. There is no one-to-one equivalent for this function in Linux, but it can be replicated using the following logic:

  • fork() to create a new child process with new PID
  • setuid() to switch to the new PID
  • exec() to change the existing process image with the process to execute

Terminating a process
To forcibly terminate a running process, you can use TerminateProcess() in Windows.


BOOL TerminateProcess(
  HANDLE hProcess, // handle to the process
  UINT uExitCode   // exit code for the process
);

This function terminates the running process and all the associated threads. Use this function only in extreme scenarios.

In Linux, you can use kill() to forcibly kill a process: int kill(pid_t pid, int sig). This system call terminates the process of id PID. You can also use it to signal to any group or process.

Using wait functions
In cases when the child process is dependent on the parent process, you can use wait functions in parent process to wait for the child process termination. In Windows, you can use the WaitForSingleObject() function call to achieve this.

You can use the WaitForMultipleObject() function to wait for more than one object.


DWORD WaitForMultipleObjects(
  DWORD nCount,             // number of handles in array
  CONST HANDLE *lpHandles,  // object-handle array
  BOOL bWaitAll,            // wait option
  DWORD dwMilliseconds      // time-out interval
);

You can populate the object-handle array with the number of objects to wait for. Based on the bWaitALL option, you can either wait for all the objects to be signaled or wait for any of them to be signaled.

In both of these functions, if you want to wait for a finite time, you can specify the time interval in the second parameter. If you want to wait infinitely, use INFINITE as the value for dwMilliseconds. Setting dwMilliseconds to 0 will just test the state of the object and return.

You can use waitpid() in Linux if you want to just wait infinitely for the process to die. In Linux, there is no way to do a timed wait on a waitpid() call.

In this code: pid_t waitpid(pid_t pid, int *status, int options), waitpid() infinitely waits for the child process to terminate. Wait functions, in both Windows and Linux, suspend the execution of the current process until it completes, but in Windows there is an option to exit by specifying a time value. You can implement a timed wait or NO WAIT functionality similar to WaitForSingleObject() and WaitForMultipleObject() using System V semaphores, which is discussed in Part 2 of this series. Part 3 of this series further discusses wait functions.

Exiting a process
Exiting a process means a graceful exiting of the process with a proper cleanup. In Windows, you use ExitProcess() to perform this operation.


VOID ExitProcess(
  UINT uExitCode   // exit code for all threads
);

ExitProcess() is the preferred method of ending a process. This function provides a clean process shutdown. This includes calling the entry-point function of all attached dynamic-link libraries (DLLs) with a value indicating that the process is detaching from the DLL.

The Linux equivalent for ExitProcess() is exit(): void exit(int status);.

The exit() function causes normal program termination and the value of status &0377 is returned to the parent. The C standard specifies two definitions (EXIT_SUCCESS and EXIT_FAILURE) that can be passed to the status parameter to indicate successful or unsuccessful termination.

Environment variables
Each process has an environment block associated with it, basically name=value pairs that specify various environments the process can access. Even though we can specify the environment when we create the process, there are also specific functions to set and obtain environment variables after the process is created.

In Windows, you can use GetEnvironmentVariable() and SetEnvironmentVariable() to get and set the environment variables.


DWORD GetEnvironmentVariable(
  LPCTSTR lpName,  // environment variable name
  LPTSTR lpBuffer, // buffer for variable value
  DWORD nSize      // size of buffer
);

This function returns the size of the value buffer on success and 0 if the name specified is not a valid environment variable name. The SetEnvironmentVariable() function sets the contents of the specified environment variable for the current process.


BOOL SetEnvironmentVariable(
  LPCTSTR lpName,  // environment variable name
  LPCTSTR lpValue  // new value for variable
);

If the function succeeds, the return value is non-zero. If the function fails, the return value is zero.

In Linux, getenv() and setenv() system calls provide the equivalent functionality.


char *getenv(const char *name);
int setenv(const char *name, const char *value, int overwrite);

The getenv() function searches the environment list for a string that matches the string pointed to by name. This function returns a pointer to the value in the environment or NULL if there is no match. The setenv() function adds the variable name to the environment with the value if the name does not already exist. If the name does exist in the environment, then its value is changed to value if overwrite is non-zero. If overwrite is zero, then the value of name is not changed. The setenv() function returns zero on success or -1 if there was insufficient space in the environment.

Examples
The following examples illustrate what we've discussed in this section.

Listing 1. Windows process code

//Sample Application that explain process concepts

//Parameters Declaration/Definition

int TimetoWait;
STARTUPINFO si;
PROCESS_INFORMATION pi;
LPTSTR lpszCurrValue,LPTSTR lpszVariable;
TCHAR tchBuf[BUFSIZE];
BOOL fSuccess;

if(argc > 2)
{
    printf("InvalidArgument");
    ExitProcess(1); //Failure
}

//Get and display an  environment variable PATH
lpszCurrValue = ((GetEnvironmentVariable("PATH",tchBuf, BUFSIZE) > 0) ? tchBuf : NULL);
lpszVariable = lpszCurrValue;

//Display the environment variable
while (*lpszVariable)
    putchar(*lpszVariable++);
putchar('\n');

//Initialise si and pi
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
ZeroMemory( &pi, sizeof(pi) );

//Create a childProcess
if( !CreateProcess( NULL,             // No module name (use command line).
                    "SomeProcess",    // Command line.
                    NULL,             // Process handle not inheritable.
                    NULL,             // Thread handle not inheritable.
                    FALSE,            // Set handle inheritance to FALSE.
                    0,                // No creation flags.
                    NULL,             // Use parent's environment block.
                    NULL,             // Use parent's starting directory.
                    &si,              // Pointer to STARTUPINFO structure.
                    &pi )             // Pointer to PROCESS_INFORMATION structure.
                        )
{
    printf( "CreateProcess failed." );
}

// Wait until child process exits.
if(argc == 2)
{
    TIMEOUT = atoi(argv[1]);
    ret = WaitForSingleObject( pi.hProcess, TIMEOUT );
    if(ret == WAIT_TIMEOUT)
    {
        TerminateProcess(pi.hProcess);
    }
}
else
{
    WaitForSingleObject( pi.hProcess, INFINITE );
    ...
}
    ExitProcess(0); //Success

Listing 2. Equivalent Linux process code

#include <stdlib.h>

int main(int argc,char *argv[])
{
    //Parameters Declaration/Definition


    char PathName[255];
    char *Argptr[20];
    int rc;
    char *EnvValue,*lpszVariable;

    if(argc > 1)
    {
        printf(" Wrong parameters !!");
        exit(EXIT_FAILURE);
    }
    //Get and display an  environment variable PATH
    EnvValue = getenv("PATH");
    if(EnvValue == NULL)
    {
        printf("Invalid environment variable passed as param !!");
    }else
    {
        lpszVariable = EnvValue;
        while (*lpszVariable)
            putchar(*lpszVariable++);
        putchar('\n');
    }
    rc = fork(); //variable rc's value on success would be process ID in the parent
                 //process, and 0 in the child's thread of execution.
    switch(rc)
    {
        case -1:
            printf("Fork() function failed !!");
            ret = -1;
            break;
        case 0:
            printf("Child process...");
            setpgid(0,0);  //Change the parent grp ID to 0


        ret = execv(PathName,Argptr); // there are other flavours of exec available,
                                      // u can use any of them based on the arguments.
        if(ret == -1)
        {
            kill(getpid(),0);
        }
        break;
         default:
             // infinitely waits for child process to die
             Waitpid(rc,&status,WNOHANG);
             //Note RC will have PID returned since this is parent process.

             break;
    }
    exit(EXIT_SUCCESS);
}

Threads
In Windows, the thread is the basic unit of execution. One or more threads run in the context of the process. The scheduling code is implemented in the kernel. There is no single "scheduler" module or routine.

The Linux kernel uses a process model rather than a threading model. The Linux kernel provides a lightweight process framework for creating threads; the actual thread implementation is in the user space. There are various threading libraries available (LinuxThreads, NGPT, NPTL, and so on) in Linux. The information in this article is based on the LinuxThreads library, but the information here is also applicable to Red Hat's Native POSIX Threading Library (NPTL).

This section describes threading in Windows and in Linux. It covers the calls for creating a thread, setting its attributes, and changing its priority.

Table 2. Thread mapping
Windows Linux Classification
CreateThread pthread_create
pthread_attr_init
pthread_attr_setstacksize
pthread_attr_destroy
Mappable
ThreadExit pthread_exit Mappable
WaitForSingleObject pthread_join
pthread_attr_setdetachstate
pthread_detach
Mappable
SetPriorityClass
SetThreadPriority
setpriority
sched_setscheduler
sched_setparam

pthread_setschedparam
pthread_setschedpolicy
pthread_attr_setschedparam
pthread_attr_setschedpolicy
Context Specific

Creating a thread
In Windows, you can use CreateThread() to create a thread to execute under the virtual address space of the calling process.


HANDLE CreateThread(
  LPSECURITY_ATTRIBUTES lpThreadAttributes,     // SD
  SIZE_T dwStackSize,                           // initial stack size
  LPTHREAD_START_ROUTINE lpStartAddress,        // thread function
  LPVOID lpParameter,                           // thread argument
  DWORD dwCreationFlags,                        // creation option
  LPDWORD lpThreadId                            // thread identifier
);

lpThreadAttributes is a pointer to the thread attributes that determines whether the thread handle can be inherited by the child process.

Linux uses the pthread library call pthread_create() to spawn a thread:


int pthread_create (pthread_t *thread_id, pthread_attr_t *threadAttr,
                    void * (*start_address)(void *), void * arg);

Note: In Windows, the number of threads a process can create is limited by the available virtual memory. By default, every thread has one megabyte of stack space. Therefore, you can create at most 2,028 threads. If you reduce the default stack size, you can create more threads. In Linux, the maximum number of process per user can be found using ULIMIT -a (limits for all users), and you can update it by using ULIMIT -u, but it would be valid only for that logon. The header files under /usr/Include/limit.h and ulimit.h define these constants. You can modify them and recompile kernel to hv permanent effect. For POSIX threadlimits, the THREAD_THREADS_MAX macro defines the maximum limit and is defined in local_lim.h.

Specifying the thread function
The parameter lpStartAddress in the CreateThread() is the address of the function that the newly created thread will execute.

The parameter start_address for the Linux library call pthread_create() is the address of the function that the newly created thread will execute.

Parameter passing to the thread function
In Windows, the parameter lpParameter for the system call CreateThread() specifies the parameter to be passed to the newly created thread. It specifies the address of the data item to be passed to the new thread.

In Linux, the parameter arg for the library call pthread_create() specifies the parameter to be passed to the new thread.

Setting the stack size
In Windows, the parameter dwStackSize for the CreateThread() is the size of stack in bytes that is to be allocated for the new thread. The stack size should be a non-zero multiple of 4 KB and a minimum of 8 KB.

In Linux, the stack size is set in the pthread attributes object; that is, the parameter threadAttr of type pthread_attr_t is passed to the library call pthread_create(). This object needs to be initialized by the call pthread_attr_init() before any attributes are set. The attribute object is destroyed using the call pthread_attr_destroy():


int pthread_attr_init(pthread_attr_t *threadAttr);
int pthread_attr_destroy(pthread_attr_t *threadAttr);

Note that all of the pthread_attr_setxxxx calls achieve similar functionality to the pthread_xxxx calls (if available) except that you can use pthread_attr_xxxx only before thread creation to update the attribute object that will be passed as a parameter to pthread_create. Meanwhile, you can use pthread_xxxx calls at any time after the thread has been created.

The stack size is set using the call pthread_attr_setstacksize(): int pthread_attr_setstacksize(pthread_attr_t *threadAttr, int stack_size);.

Exiting a thread
In Windows, the system call ExitThread() terminates the thread. The dwExitCode is the return value of the thread, and it can be retrieved from another thread by calling GetExitCodeThread().


VOID ExitThread(
  DWORD dwExitCode   // exit code for this thread
);

The Linux equivalent for this is the library call pthread_exit(). The retval is the return value of the thread, and you can retrieve it from another thread by calling pthread_join(): int pthread_exit(void* retval);.

Thread states
In Windows, there are no explicit thread states maintained with respect to thread termination. However, WaitForSingleObject() allows a thread to wait explicitly on the termination of a specific or non-specific thread within the process.

In Linux, threads are by default created in joinable state. In joinable state, another thread can synchronize on the thread's termination and recover its termination code using the function pthread_join(). The thread resources of the joinable thread are released only after it is joined.

Windows uses WaitForSingleObject() to wait for a thread to terminate:


DWORD WaitForSingleObject(
  HANDLE hHandle,
  DWORD dwMilliseconds
);

Where:

  • hHandle is the pointer to the thread handle.
  • dwMilliseconds is the time out value in milliseconds. If the value is set to INFINITE, then it blocks the calling thread/process indefinitely.

Linux uses pthread_join() to do the same: int pthread_join(pthread_t *thread, void **thread_return);.

In the detached state, the thread resources are immediately freed when it terminates. The detached state can be set by calling pthread_attr_setdetachstate() on the thread attribute object: int pthread_attr_setdetachstate (pthread_attr_t *attr, int detachstate);. A thread created in a joinable state can later be put into a detached state using the pthread_detach() call: int pthread_detach (pthread_t id);.

Changing priority
In Windows, the priority of the thread is determined by the priority class of its process and the priority level of the thread within the priority class of the process. In Linux, the thread itself is the unit of execution and has its own priority. It has no dependency on the priority of its process.

In Windows, you can use SetPriorityClass() to set the priority class for the specified process:


BOOL SetPriorityClass(
  HANDLE hProcess,         // handle to the process
  DWORD dwPriorityClass    // Priority class
);

dwPriorityClass is the priority class of the process, and it is set to any of the following values:

  • IDLE_PRIORITY_CLASS
  • BELOW_NORMAL_PRIORITY_CLASS
  • NORMAL_PRIORITY_CLASS
  • ABOVE_NORMAL_PRIORITY_CLASS
  • HIGH_PRIORITY_CLASS
  • REALTIME_PRIORITY_CLASS

Once the priority class of the process is set, SetThreadPriority() is used to set the priority level of the thread within the priority class of the process:


BOOL SetThreadPriority(
  HANDLE hThread,
  int nPriority
);

nPriority is the priority value of the thread, and it is set to one of the following values:

  • THREAD_PRIORITY_ABOVE_NORMAL sets the priority to 1 point above the priority class.
  • THREAD_PRIORITY_BELOW_NORMAL sets the priority to 1 point below the priority class.
  • THREAD_PRIORITY_HIGHEST sets the priority to 2 points above the priority class.
  • THREAD_PRIORITY_IDLE sets base priority to 1 for IDLE_PRIORITY_CLASS, BELOW_NORMAL_PRIORITY_CLASS, NORMAL_PRIORITY_CLASS, ABOVE_NORMAL_PRIORITY_CLASS, or HIGH_PRIORITY_CLASS processes, and sets base priority to 16 for REALTIME_PRIORITY_CLASS processes.
  • THREAD_PRIORITY_LOWEST sets the priority to 2 points below the priority class.
  • THREAD_PRIORITY_NORMAL sets to normal priority for the priority class.
  • THREAD_PRIORITY_TIME_CRITICAL sets the base priority to 15 for IDLE_PRIORITY_CLASS, BELOW_NORMAL_PRIORITY_CLASS, NORMAL_PRIORITY_CLASS, ABOVE_NORMAL_PRIORITY_CLASS, or HIGH_PRIORITY_CLASS processes, and sets base priority to 31 for REALTIME_PRIORITY_CLASS.

Examples of processes and threads
To wrap up this installment, let's look at some examples of the following types of processes and threads:

  • Normal or regular processes and threads
  • Time-critical and real-time processes and threads

Normal or regular processes/threads
The Linux system call setpriority() is used to set or modify priority levels for normal processes and threads. The parameter scope is PRIO_PROCESS. Set id to 0 to change the current process (or thread) priority. Again, delta is the priority value -- this time in the range -20 to 20. Note also that in Linux, a lower delta value means a higher priority. So you set +20 for IDLETIME priority and 0 for REGULAR priority.

In Windows, the priority range is from 1 (lower priority) to 15 (higher priority) for the regular threads. But in Linux, the priority range for normal non-real-time processes is from -20 (higher) to +20 (lower priority). This has to be mapped before being used: int setpriority(int scope, int id, int delta);.

Time-critical and real-time processes and threads
You can use the Linux system call sched_setscheduler() to change the scheduling priority and policy of a running process: int sched_setscheduler(pit_t pid, int policy, const struct sched_param *param);.

The parameter policy is the scheduling policy. The possible values for policy are SCHED_OTHER (for regular non-real-time scheduling), SCHED_RR (real-time round-robin policy), and SCHED_FIFO (real-time FIFO policy).

Here, param is a pointer to a structure representing scheduling priority. It can range from 1 to 99 only for real-time policies. For others (normal non-real-time processes), it is zero.

In Linux, for a known scheduling policy, it is also possible to change only the process priority by using the system call sched_setparam: int sched_setparam(pit_t pid, const struct sched_param *param);.

The LinuxThreads library call pthread_setschedparam is the thread version of sched_setscheduler and is used to dynamically change the scheduling priority and policy for a running thread: int pthread_setschedparam(pthread_t target_thread, int policy, const struct sched_param *param);.

The parameter target_thread indicates the thread whose priority is to be changed; param indicates the priority.

The LinuxThreads library calls pthread_attr_setschedpolicy, and you can use pthread_attr_setschedparam to set the scheduling policy and the priority level to the thread attribute object before the thread is created:


int pthread_attr_setschedpolicy(pthread attr_t *threadAttr, int policy);

int pthread_attr_setschedparam(pthread attr_t *threadAttr, const struct sched_param *param);

In Windows, the priority range is from 16 (lower priority) to 31 (higher priority) for the real-time threads. In Linux, the priority range for real-time threads is from 99 (higher) to 1 (lower priority). This has to be mapped before being used.

Examples
The following listings illustrate the concepts in this section.

Listing 3. Windows thread example
Main Thread

enum stackSize = 120 * 1024 ;
// create a thread normal and real time thread
DWORD  normalTId, realTID;
HANDLE normalTHandle, realTHandle;

normalTHandle = CreateThread(
                NULL,            // default security attributes
                stackSize,       // 120K
                NormalThread,    // thread function
                NULL,            // argument to thread function
                0,               // use default creation flags
                &normalTId);     // returns the thread identifier

// Set the priority class as "High priority"
SetPriorityClass(pHandle, HIGH_PRIORITY_CLASS);

normalTHandle = CreateThread(
                 NULL,            // default security attributes
                 stackSize,       // 120K
                 NormalThread,    // thread function
                 NULL,            // argument to thread function
                 0,               // use default creation flags
                &normalTId);     // returns the thread identifier

CloseHandle(threadHandle);
...
...

// Thread function
DWORD WINAPI NormalThread ( LPVOID lpParam )
{

    HANDLE tHandle,pHandle;

    pHandle = GetCurrentProcess();
    tHandle = GetCurrentThread();

    // Set the priority class as "High priority"
    SetPriorityClass(pHandle, HIGH_PRIORITY_CLASS);
    // increase the priority by 2 points above the priority class
    SetThreadPriority(tHandle,THREAD_PRIORITY_HIGHEST);

    // perform job at high priority
    ...
    ...
    ...

    // Reset the priority class as "Normal"
   SetPriorityClass(pHandle, NORMAL_PRIORITY_CLASS);

    // set the priority back to normal
    SetThreadPriority(tHandle,THREAD_PRIORITY_NORMAL);

    // Exit thread
    ExitThread(0);

}

// Thread function
DWORD WINAPI RealTimeThread ( LPVOID lpParam )
{
    HANDLE tHandle, pHandle ;
    pHandle = GetCurrentProcess();
    tHandle = GetCurrentThread  ();

    // Set the priority class as "Real time"
   SetPriorityClass(pHandle, REALTIME_PRIORITY_CLASS);

    // increase the priority by 2 points above the priority class
    SetThreadPriority(tHandle,THREAD_PRIORITY_HIGHEST);

    // do time critical work
    ...
    ...
    ...
    // Reset the priority class as "Normal"
   SetPriorityClass(pHandle, NORMAL_PRIORITY_CLASS);

    // Reset the priority back to normal
    SetThreadPriority(tHandle,THREAD_PRIORITY_NORMAL);

    ExitThread(0);
}

Listing 4. Equivalent thread code in Linux

static void * RegularThread (void *);
static void * CriticalThread (void *);

// Main Thread

       pthread_t thread1, thread2;  // thread identifiers
       pthread_attr_t threadAttr;

       struct sched_param param;  // scheduling priority

       // initialize the thread attribute
       pthread_attr_init(&threadAttr);

       // Set the stack size of the thread
       pthread_attr_setstacksize(&threadAttr, 120*1024);

       // Set thread to detached state. No need for pthread_join
       pthread_attr_setdetachstate(&threadAttr, PTHREAD_CREATE_DETACHED);

       // Create the threads
       pthread_create(&thread1, &threadAttr, RegularThread, NULL);
       pthread_create(&thread2, &threadAttr, CriticalThread,NULL);

       // Destroy the thread attributes
       pthread_attr_destroy(&threadAttr);
       ...
       ...



  // Regular non-realtime Thread function
  static void * RegularThread (void *d)   {

       int priority = -18;

       // Increase the priority
       setpriority(PRIO_PROCESS, 0, priority);

       // perform high priority job
       ...
       ...

       // set the priority back to normal
       setpriority(PRIO_PROCESS, 0, 0);

       pthread_exit(NULL);

   }


   // Time Critical Realtime Thread function
   static void * CriticalThread (void *d) {

       // Increase the priority

        struct sched_param param;  // scheduling priority
        int policy = SCHED_RR;     // scheduling policy

        // Get the current thread id

        pthread_t thread_id = pthread_self();

        // To set the scheduling priority of the thread
        param.sched_priority = 90;
        pthread_setschedparam(thread_id, policy, &param);

        // Perform time critical task
        ...
        ...

        // set the priority back to normal

        param.sched_priority = 0;
        policy = 0;             // for normal threads
        pthread_setschedparam(thread_id, policy, &param);
        ....
        ....

        pthread_exit(NULL);
   }

Next in the series
This first part of the series has given you a guide to help map Windows processes and threads to their functional counterparts in Linux. Part 2 in the series covers synchronization objects and primitives, starting with semaphores and events. Part 3 covers mutexes, critical sections, and wait functions.

Resources

About the authors
Author photoSrinivasan S. Muthuswamy works as a Software Engineer for IBM Global Services Group. He joined IBM in 2000 and his expertise in programming reaches from scripting languages to object- and procedure-oriented languages on multiple platforms (Linux, Windows, WebSphere, Lotus, and so on). Muthuswamy has developed solutions ranging from system programming on Linux and Windows to Web solutions for J2EE. His primary focus is on integration and porting and he holds a B.Eng. in Computer Engineering from the Government College of Technology, Coimbatore, India. You can contact him at smuthusw@in.ibm.com.


Author photoKavitha Varadarajan has worked as a software Engineer in the IBM India Software Lab from December 2000. Her work experience involves development and support of host-access client products such as PCOMM and networking software such as the communication server. Varadarajan has a hands-on experience with a migration project that involves porting object-oriented IPC Windows applications to Linux. She holds a B.Eng. in Computer Science and Engineering from Shanmugha College of Engineering, Tanjore, India. She can be contacted at vkavitha@in.ibm.com.



e-mail it!
Rate this article

This content was helpful to me:

Strongly disagree (1) Disagree (2) Neutral (3) Agree (4) Strongly agree (5)

Comments?



developerWorks > Linux >
developerWorks
    About IBM Privacy Contact